
/*
    ThreeDPanel.m

    ThreeDPanel is a very simple panel for controlling a few attributes of the
    3D graphs.  A real app would have a much better UI for controlling 3D
    presentation.
    
    There is only one instance of the panel, which tracks the
    setting of the main window.  When a document's window becomes main, it
    sends the setCamera: method to the panel, thus keeping it up to date.

    You may freely copy, distribute, and reuse the code in this example.
    NeXT disclaims any warranty of any kind, expressed or implied, as to its
    fitness for any particular use.
*/

#import "Graph.h"

/* declare methods private to this class's implementation */
@interface ThreeDPanel(Private)
- (void)_sendNotification;
@end

@implementation ThreeDPanel

/* called when the surface color well changes */
- changeSurfaceColor:sender {
    [[camera worldShape] setColor:[sender color]];
    [camera display];
    [self _sendNotification];
    return self;
}

/* called when the background color well changes */
- changeBackgroundColor:sender {
    [camera setBackgroundColor:[sender color]];
    [camera display];
    [self _sendNotification];
    return self;
}

/* called when shading buttons are changed */
- changeShading:sender {
    static shadeTable[4] = {N3D_PointCloud, N3D_WireFrame, N3D_FacetedSolids, N3D_SmoothSolids};

  /*
   * This message gets the right hidden-surface algorithm for the new type
   * of shading.  If you just set the surface type of the individual shapes,
   * you can end of with the sides of the objects drawn out of order because
   * the correct hider was not chosen.
   */
    [camera setSurfaceTypeForAll:shadeTable[[shadingButtons selectedRow]] chooseHider:YES];
    [camera display];
    [self _sendNotification];
    return self;
}

/*
 * Called when the zoom slider is changed.  We just move the eye point along
 * its current vector, at a distance given by the slider's value.
 */
- changeZoom:sender {
    RtPoint from, to;
    float distance;
    float angle;
    float newDistance;

    [camera getEyeAt:&from toward:&to roll:&angle];
    distance = sqrt(from[0]*from[0] + from[1]*from[1] + from[2]*from[2]);
    newDistance = [sender floatValue];
    if (newDistance < 0.01)
	newDistance = 0.01;
    from[0] *= newDistance / distance;
    from[1] *= newDistance / distance;
    from[2] *= newDistance / distance;
    [camera setEyeAt:from toward:to roll:angle];

    [camera display];
    [self _sendNotification];
    return self;
}

/*
 * Associates the panel with a new camera view.  Causes the panel's controls
 * to adjust to reflect the current settings of the camera and its world shape
 * (top level shape).
 */
- setCamera:obj {
    RtPoint fromPoint;
    RtPoint toPoint;
    float aRollAngle;

    camera = obj;
    [surfaceColorWell setEnabled:obj != nil];
    [backgroundColorWell setEnabled:obj != nil];
    [shadingButtons setEnabled:obj != nil];
    [zoomSlider setEnabled:obj != nil];
    if (camera) {
	[surfaceColorWell setColor:[[camera worldShape] color]];
	[backgroundColorWell setColor:[camera backgroundColor]];
	switch ([[camera worldShape] surfaceType]) {
	    case N3D_PointCloud:
		[shadingButtons selectCellAt:0 :0];
		break;
	    case N3D_WireFrame:
		[shadingButtons selectCellAt:1 :0];
		break;
	    case N3D_FacetedSolids:
		[shadingButtons selectCellAt:2 :0];
		break;
	    case N3D_SmoothSolids:
		[shadingButtons selectCellAt:3 :0];
		break;
	    default:
		NX_ASSERT(NO, "Unexpected shading type in setCamera:");
	}
	[camera getEyeAt:&fromPoint toward:&toPoint roll:&aRollAngle];
	[zoomSlider setFloatValue:sqrt(fromPoint[0]*fromPoint[0] + fromPoint[1]*fromPoint[1] + fromPoint[2]*fromPoint[2])];
    }
    return self;
}

- (void)_sendNotification {
    id docDelegate = [[camera window] delegate];

    if ([docDelegate respondsTo:@selector(threeDPanelDidChangeDoc:)]) {
	[docDelegate threeDPanelDidChangeDoc:self];
    }
}

@end

